#pragma once
#include<iostream>
#include<functional>
#include<string>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include"log.hpp"
#include"protocol.hpp"
namespace server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        LISTEN_ERR,
        ACCEPT_ERR
    };

    // req是输入型参数，resp是输出型参数！
    typedef std::function<bool(const Request& req,Respone& resp)> func_t;

    //保证解耦
    void handlerEnter(int sock,func_t func)
    {
        std::string inbuffer;
        while(true)
        {
            // 如果按照以前的写法！我们现在就要在套接字里面进读取了！
            // 然后将取到的消息如何如何
            // 但是现在
            // 因为我们是服务端未来收到的一定是一个或者多个的报文！

            // 1.现在我们该怎么保证我们读取到的消息是一个[完整]的报文(请求)呢？
            // 因为tcp是面向字节流的！所以我们要明确报文和报文的边界！
            // 因为我们今天是一个多执行流的代码！所以我们等得起！不害怕阻塞！

            std::string req_text;
            if (!recvPackage(sock, inbuffer,&req_text)) // 如何一直没有拿到完整的请求则会一直读取！
            {
                std::cout << "recv fail!" <<std::endl;
                // 万一读取失败！
                return;
            }
            std::cout << "带包头的请求: \n" << req_text << std::endl;
            // 到这里我们一定会得到一个完整的请求！
            // content_len\r\xxxxx xxxx\r\n

            std::string req_str;
            if (!deLength(req_text, &req_str))
            {
                std::cout << "deLength fail!" << std::endl;
                return; // 去获取报文内容！
            }

            std::cout << "去掉报头的正文: \n" << req_str<< std::endl;

            // 2. 对请求反序列化！
            // 2.1得到一个结构化的Request对象
            Request req;
            if (!req.Deserialize(req_str))
            {
                std::cout << "反序列化失败！" << std::endl;
                return; // 反序列化失败
            }
            std::cout << "反序列化成功！" << std::endl;


            // 3.计算处理业务！——这里才是真正的业务逻辑！上面和下面都是处理IO的！
            // 3.1 根据计算结果得到一个结构化的响应！Response对象！
            // 这部分因为和上下都没有什么关联！我们可以解耦开来！
            Respone resp;
            func(req, resp); // 将函数的处理结果都放入resp里面！
            std::cout << "handler success" << std::endl;

            // 4.对响应对象进行序列化！
            // 得到了一个字符串！
            std::string resp_str;
            resp.Serialize(&resp_str);
            std::cout<< "计算完成！序列化响应！" << resp_str << std::endl;

            // 5.最后我们再发送响应
            // 5.1构建成一个完整的报文！
            std::string send_string = enLength(resp_str);
            std::cout<< "构建完整的响应！\n" << send_string << std::endl;

            send(sock, send_string.c_str(), send_string.size(), 0); // send是用于流式套接字的发送！作用和write一样！
            // 这里的发送也是有问题的！——但是我们这里先主要解决读的问题！写的问题后面在说！


        }
    }

    const static int gbacklog = 5;
    const static uint16_t gport = 8080;
    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport)
            : port_(port), listensock_(-1)
        {
        }

        void initServer()
        {
            //1.首先就是要创建套接字
            //1.1创建一个套接字！
            listensock_ = socket(AF_INET,SOCK_STREAM,0);
            if(listensock_ == -1)
            {
                LogMessage(FATAL,"creat socket fail! ");
                exit(SOCKET_ERR);
            }

            LogMessage(NORMAL, "creat socket sucess! ");
            //1.2进行绑定！
            struct sockaddr_in peer;
            bzero(&peer,sizeof(peer));
            peer.sin_family = AF_INET;
            peer.sin_addr.s_addr = INADDR_ANY;
            peer.sin_port = htons(port_);
            int n = bind(listensock_,(struct sockaddr*)&peer,sizeof(peer));

            if(n == -1)
            {
                LogMessage(FATAL, "bind socket fail! ");
                exit(BIND_ERR);
            }

            LogMessage(NORMAL, "bind socket sucess! ");


            //3.设置socket 为监听状态！
           if (listen(listensock_, gbacklog) <  0)
           {
               LogMessage(FATAL, "listen socket fail! "); 
               exit(LISTEN_ERR);
           }
        
            LogMessage(NORMAL, "listen socket sucess! ");//这是一个正常的常规日志！
        }

        void start(func_t func)
        {

            for (;;)
            {
                signal(SIGCHLD, SIG_IGN); // 直接忽略子进程信号,那么操作系统就会自动回收

                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                int sock = accept(listensock_, (struct sockaddr *)&peer, &len);

                std::cout << sock << std::endl;
                if (sock == -1)
                {
                    LogMessage(ERROR, "accept fail! next"); 

                    continue; 
                }
                LogMessage(NORMAL, "accept a link success!"); 

                pid_t id = fork();
                if (id == 0)
                {

                    close(listensock_);
                    handlerEnter(sock,func);

                    close(sock);
                    exit(0);
                }
                close(sock);

                // pid_t ret = waitpid(id, nullptr, 0);
                // if (ret > 0)
                // {
                //     LogMessage(NORMAL, "wait success!");
                // }
            }
        }
        ~TcpServer()
        {
        }
    private:
        int listensock_; // tcp服务端也是要有自己的socket的！这个套接字的作用不是用于通信的！而是用于监听连接的！
        uint16_t port_;//tcp服务器的端口

    };


}